package org.extdev.protocol.checksumm;

import org.extdev.protocol.unsigned.UnsignedShort;

public class CRC16 implements CheckSumm {

	private short poly;
	private short startFill;
	private short xorOut = 0;
	private boolean isReverse;
	private boolean moduleCheck;
	
	public CRC16(short poly, short startFill, boolean moduleCheck, boolean reverse) {
		setPolynom(poly);
		setStartFill(startFill);
		setModuleCheck(moduleCheck);
		setReverseBytes(reverse);
	}
	
	public CRC16(short poly, short startFill) {
		this(poly, startFill, false, false);
	}
	
	public CRC16(short poly) {
		this(poly, (short) -1);
	}
	
	public CRC16(boolean reverese) {
		this((short) 0xA001, (short) -1,false,  reverese);
	}
	
	public CRC16() {
		this((short) 0xA001, (short) -1, false, false);
	}
	
	@Override
	public Byte[] doHash(Byte[] a) {
		UnsignedShort checkSumm = new UnsignedShort(startFill);
		for(byte data : a) {
			checkSumm.xor(data);
			for(int i = 0; i < 8; i++) {
				boolean flag = checkSumm.getBitValue(0);
				checkSumm.unsignedRightShift(1);
				if(flag) {
					checkSumm.xor(poly);
				}
			}
		}
		return getResult(getOutCheck(checkSumm));
	}
	
	private UnsignedShort getOutCheck(UnsignedShort crc) {
		if(moduleCheck) {
			crc.xor(xorOut);
		}
		return crc;
	}
	
	private Byte[] getResult(UnsignedShort crc) {
		Byte[] ret = new Byte[2];
		if(isReverse) {
			ret[0] = crc.getByte(1).getByte();
			ret[1] = crc.getByte(0).getByte();
		}
		else {
			ret[1] = crc.getByte(1).getByte();
			ret[0] = crc.getByte(0).getByte();
		}
		return ret;
	}
	
	public void setPolynom(short polynom) {
		this.poly = polynom;
	}
	
	public void setStartFill(short fill) {
		startFill = fill;
	}
	
	public void setReverseBytes(boolean reverse) {
		isReverse = reverse;
	}
	
	public boolean isReverese() {
		return isReverse;
	}
	
	public short getPolynom() {
		return poly;
	}
	
	public boolean isModuleCheck() {
		return moduleCheck;
	}
	
	public void setModuleCheck(boolean flag) {
		moduleCheck = flag;
	}
	
	public short getXorOut() {
		return xorOut;
	}
	
	public void setXorOut(short value) {
		xorOut = value;
	}
}
